#ifndef MAIN_CPP
#define MAIN_CPP
#include <fstream>
#include <iostream>
#include "device/Vector_dev.hpp"
#include "Renderer.hpp"
#include "Triangle.hpp"
#include "Material.hpp"
#include "Scene.hpp"
#include "Object.hpp"
#include <chrono>

Scene *create_scene()
{
    // Change the definition here to change resolution
    Scene *scene = new Scene();

    Material *red = new Material(DIFFUSE, Vector3fDev(0.0f, 0.0f, 0.0f));
    red->Kd = Vector3fDev(0.63f, 0.065f, 0.05f);
    Material *green = new Material(DIFFUSE, Vector3fDev(0.0f, 0.0f, 0.0f));
    green->Kd = Vector3fDev(0.14f, 0.45f, 0.091f);
    Material *white = new Material(DIFFUSE, Vector3fDev(0.0f, 0.0f, 0.0f));
    white->Kd = Vector3fDev(0.725f, 0.71f, 0.68f);
    Material *light = new Material(DIFFUSE, (Vector3fDev(0.747f + 0.058f, 0.747f + 0.258f, 0.747f) * 8.0f + Vector3fDev(0.740f + 0.287f, 0.740f + 0.160f, 0.740f) * 15.6f + Vector3fDev(0.737f + 0.642f, 0.737f + 0.159f, 0.737f) * 18.4f));
    light->Kd = Vector3fDev(0.65f, 0.65f, 0.65f);

    MeshTriangle *floor = new MeshTriangle("../models/cornellbox/floor.obj", white);
    MeshTriangle *shortbox = new MeshTriangle("../models/cornellbox/shortbox.obj", white);
    MeshTriangle *tallbox = new MeshTriangle("../models/cornellbox/tallbox.obj", white);
    MeshTriangle *left = new MeshTriangle("../models/cornellbox/left.obj", red);
    MeshTriangle *right = new MeshTriangle("../models/cornellbox/right.obj", green);
    MeshTriangle *light_ = new MeshTriangle("../models/cornellbox/light.obj", light);

    scene->Add_object(floor);
    scene->Add_object(shortbox);
    scene->Add_object(tallbox);
    scene->Add_object(left);
    scene->Add_object(right);
    scene->Add_object(light_);

    scene->buildBVH();
    return scene;
}

int main()
{
    Scene *scene = create_scene();
    Vector3fDev *framebuffer = new Vector3fDev[scene->width * scene->height];

    Vector3fDev eye_pos(278.F, 273.F, -800.F);
    int m = 0;

    // change the spp value to change sample ammount
    int spp = 64;
    std::cout << "SPP: " << spp << "\n";

    Renderer renderer;
    auto start = std::chrono::system_clock::now();
    renderer.Render_GPU(scene, spp, eye_pos, framebuffer);
    auto stop = std::chrono::system_clock::now();

    std::cout << "Render complete: \n";
    std::cout << "Time taken: " << std::chrono::duration_cast<std::chrono::hours>(stop - start).count() << " hours\n";
    std::cout << "          : " << std::chrono::duration_cast<std::chrono::minutes>(stop - start).count() << " minutes\n";
    std::cout << "          : " << std::chrono::duration_cast<std::chrono::seconds>(stop - start).count() << " seconds\n";

    // save framebuffer to file
    FILE *fp = fopen("binary.ppm", "wb");
    (void)fprintf(fp, "P6\n%d %d\n255\n", scene->width, scene->height);
    for (auto i = 0; i < scene->height * scene->width; ++i)
    {
        static unsigned char color[3];
        color[0] = (unsigned char)(255 * pow(clamp_host(0, 1, framebuffer[i].x), 0.6f));
        color[1] = (unsigned char)(255 * pow(clamp_host(0, 1, framebuffer[i].y), 0.6f));
        color[2] = (unsigned char)(255 * pow(clamp_host(0, 1, framebuffer[i].z), 0.6f));
        fwrite(color, 1, 3, fp);
    }
    fclose(fp);

    return 0;
}

#endif